﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Xml;

namespace FlightToolkit.SimProp
{
    public enum Format
    {
        XML = 0,
        FSXBinary = 1,
        FlightBinary = 2,
    }

    public class PropertySet : ICustomTypeDescriptor
    {
        #region ICustomTypeDescriptor Members
        public AttributeCollection GetAttributes()
        {
            return new AttributeCollection();
        }

        public string GetClassName()
        {
            return "PropertySet";
        }

        public string GetComponentName()
        {
            return "PropertySet";
        }

        public TypeConverter GetConverter()
        {
            return null;
        }

        public EventDescriptor GetDefaultEvent()
        {
            return null;
        }

        public System.ComponentModel.PropertyDescriptor GetDefaultProperty()
        {
            return null;
        }

        public object GetEditor(System.Type editorBaseType)
        {
            return null;
        }

        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return new EventDescriptorCollection(new EventDescriptor[] { });
        }

        public EventDescriptorCollection GetEvents()
        {
            return new EventDescriptorCollection(new EventDescriptor[] { });
        }

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return GetProperties();
        }

        public PropertyDescriptorCollection GetProperties()
        {
            var propertyDescriptors = this.ChildProperties.Select(x => (System.ComponentModel.PropertyDescriptor)new SimProp.PropertyDescriptor(this, x)).ToList();
            propertyDescriptors.AddRange(this.ChildSets.Select(x => new PropertySetDescriptor(this, x)));

            return new PropertyDescriptorCollection(propertyDescriptors.ToArray());
        }

        public object GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd)
        {
            return this;
        }
        #endregion

        #region Public
        public static PropertySet CreateDocument(Symbols symbols)
        {
            symbols = symbols ?? Symbols.Load(null);
            return new PropertySet(symbols, "SimBase.Document");
        }

        public static PropertySet Create(Symbols symbols, string name)
        {
            symbols = symbols ?? Symbols.Load(null);
            return new PropertySet(symbols, name);
        }

        public static PropertySet Load(Symbols symbols, Stream inputStream)
        {
            symbols = symbols ?? Symbols.Load(null);
            if (SPB.IsSPBFormat(inputStream))
            {
                return SPB.Read(symbols, inputStream);
            }
            else
            {
                return XML.Read(symbols, inputStream);
            }
        }

        public static PropertySet Load(Symbols symbols, string input)
        {
            using (var stream = File.OpenRead(input))
            {
                return Load(symbols, stream);
            }
        }

        public void Save(Stream outputStream, Format format)
        {
            if (format == Format.XML)
            {
                XML.Write(this, outputStream);
            }
            else
            {
                SPB.Write(this, outputStream, format);
            }
        }

        public void Save(string output, Format format)
        {
            using (var stream = File.Open(output, FileMode.Create, FileAccess.Write))
            {
                Save(stream, format);
            }
        }


        public string ToXML()
        {
            using (var stringWriter = new StringWriter())
            {
                using (var writer = new XmlTextWriter(stringWriter))
                {
                    writer.Formatting = Formatting.Indented;
                    this.WriteXML(null, writer);
                }

                return stringWriter.ToString();
            }
        }


        public string Name
        {
            get
            {
                return this.def.Name;
            }
        }

        public Guid DefId
        {
            get
            {
                return this.def.Id;
            }
        }

        public string NamespacedName
        {
            get
            {
                return String.Format("{0}.{1}", this.def.namespaceDef.Name, this.def.Name);
            }
        }

        public IEnumerable<Property> ChildProperties
        {
            get
            {
                return this.childProperties;
            }
        }

        public IEnumerable<PropertySet> ChildSets
        {
            get
            {
                return this.childSets;
            }
        }

        private bool IsPropertyMultipleCardinality(NamespaceDef namespaceDef, string name)
        {
            Cardinality card = Cardinality.Unknown;

            int index = this.def.propertyNames.IndexOf(name);
            if (index >= 0)
            {
                card = this.def.propertyCardinalityOverrides[index];
            }

            if (card == Cardinality.Unknown)
            {
                var propDef = namespaceDef.GetSymbolDef(name);
                card = propDef.Cardinality;
            }

            return card == Cardinality.Multiple;
        }

        public PropertySet GetChildSet(string name)
        {
            return GetChildSets(name.Split('\\'), 0).FirstOrDefault();
        }

        public IEnumerable<PropertySet> GetChildSets(string name)
        {
            return GetChildSets(name.Split('\\'), 0);
        }

        public PropertySet ChildSet(string name)
        {
            var sets = GetChildSets(name);
            return sets.Count() > 0 ? sets.First() : null;
        }

        public IEnumerable<Property> GetChildProperties(string name)
        {
            return GetChildProperties(name.Split('\\'), 0);
        }

        public Property GetChildProperty(string name)
        {
            var properties = GetChildProperties(name);
            return properties != null && properties.Count() > 0 ? properties.First() : null;
        }

        public bool RemoveChildProperty(Property prop)
        {
            return this.childProperties.Remove(prop);
        }

        public bool RemoveChildProperty(string name)
        {
            return RemoveChildProperties(name.Split('\\'), 0, false);
        }

        public bool RemoveChildProperties(string name)
        {
            return RemoveChildProperties(name.Split('\\'), 0, true);
        }

        public bool RemoveChildSet(string name)
        {
            return RemoveChildSets(name.Split('\\'), 0, false);
        }

        public bool RemoveChildSet(PropertySet set)
        {
            return this.childSets.Remove(set);
        }

        public bool RemoveChildSets(string name)
        {
            return RemoveChildSets(name.Split('\\'), 0, true);
        }

        public Property CreateChildProperty(string name)
        {
            return CreateChildProperty(name.Split('\\'), 0);
        }

        public Property CreateChildProperty(string[] names, int nameIndex)
        {
            if (nameIndex == names.Length - 1)
            {
                NamespaceDef namespaceDef;
                string name;

                this.def.namespaceDef.GetNamespaceAndName(names[nameIndex], out namespaceDef, out name);

                int index = this.def.propertyNames.FindIndex(x => x.Equals(name, StringComparison.OrdinalIgnoreCase));

                SymbolDef symDef;
                if (index < 0)
                {
                    if (this.def.policy == Policy.Inclusive)
                    {
                        throw new InvalidOperationException("Unexpected child property name '" + name + "' in set '" + this.Name + "'");
                    }

                    symDef = namespaceDef.GetSymbolDef(name);

                    if (symDef == null)
                    {
                        throw new InvalidOperationException("Could not find symbol '" + name + "' in namespace '" + namespaceDef.Name + "'" + ".  Referenced from set '" + this.Name + "'");
                    }

                    if (!(symDef is PropertyDef))
                    {
                        throw new InvalidOperationException("Symbol '" + name + "' in namespace '" + namespaceDef.Name + "' is not a property.  Referenced from set '" + this.Name + "'");
                    }

                    if (symDef.Cardinality == Cardinality.Single &&
                        this.def.properties.Contains(symDef.Id))
                    {
                        throw new InvalidOperationException("Property '" + name + "'already exists in set '" + this.Name + "'");
                    }
                }
                else
                {
                    symDef = this.def.namespaceDef.GetSymbolDef(this.def.properties[index]);

                    if (symDef == null)
                    {
                        throw new InvalidOperationException("Could not find symbol '" + name + "' in namespace '" + this.def.namespaceDef.Name + "'");
                    }

                    if (!(symDef is PropertyDef))
                    {
                        throw new InvalidOperationException("Symbol '" + name + "' in namespace '" + this.def.namespaceDef.Name + "' is not a property.  Referenced from set '" + this.Name + "'");
                    }

                    if ((this.def.propertyCardinalityOverrides[index] == Cardinality.Single ||
                        (this.def.propertyCardinalityOverrides[index] == Cardinality.Unknown && symDef.Cardinality == Cardinality.Single)) &&
                        this.childProperties.FirstOrDefault(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase)) != null)
                    {
                        throw new InvalidOperationException("Property '" + name + "'already exists in set '" + this.Name + "'");
                    }
                }

                Property childProp = new Property((PropertyDef)symDef);
                if (childProp.def.SerializeAsXmlAttribute)
                {
                    // Attribute items go at the beginning in order
                    int lastIndex = this.childProperties.FindLastIndex(x => x.def.SerializeAsXmlAttribute);
                    if (lastIndex < 0)
                    {
                        lastIndex = 0;
                    }
                    this.childProperties.Insert(lastIndex, childProp);
                }
                else
                {
                    this.childProperties.Add(childProp);
                }

                return childProp;
            }
            else
            {
                // selectedSets should only contain one at this point.
                var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                        x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

                int count = selectedSets.Count();
                System.Diagnostics.Debug.Assert(count <= 1);
                if (count == 0)
                {
                    return CreateChildSet(names[nameIndex]).CreateChildProperty(names, nameIndex + 1);
                }
                else
                {
                    System.Diagnostics.Debug.Assert(count == 1);
                    return selectedSets.First().CreateChildProperty(names, nameIndex + 1);
                }
            }
        }

        public PropertySet CreateChildSet(string name)
        {
            return CreateChildSet(name.Split('\\'), 0);
        }

        public PropertySet CreateChildSet(string[] names, int nameIndex)
        {
            if (nameIndex == names.Length - 1)
            {
                NamespaceDef namespaceDef;
                string name;

                this.def.namespaceDef.GetNamespaceAndName(names[nameIndex], out namespaceDef, out name);

                int index = this.def.propertyNames.FindIndex(x => x.Equals(name, StringComparison.OrdinalIgnoreCase));

                SymbolDef symDef;
                if (index < 0)
                {
                    if (this.def.policy == Policy.Inclusive)
                    {
                        throw new InvalidOperationException("Unexpected child property name '" + name + "' in set '" + this.Name + "'");
                    }

                    symDef = namespaceDef.GetSymbolDef(name);

                    if (symDef == null)
                    {
                        throw new InvalidOperationException("Could not find symbol '" + name + "' in namespace '" + namespaceDef.Name + "'");
                    }

                    if (!(symDef is PropertySetDef))
                    {
                        throw new InvalidOperationException("Symbol '" + symDef.Type.Name + "' in namespace '" + namespaceDef.Name + "' is not a set.  Referenced from set '" + this.Name + "'");
                    }
                }
                else
                {
                    symDef = this.def.namespaceDef.GetSymbolDef(this.def.properties[index]);

                    if (symDef == null)
                    {
                        throw new InvalidOperationException("Could not find symbol '" + name + "' in namespace '" + this.def.namespaceDef.Name + "'");
                    }

                    if (!(symDef is PropertySetDef))
                    {
                        throw new InvalidOperationException("Symbol '" + symDef.Type.Name + "' in namespace '" + this.def.namespaceDef.Name + "' is not a set.  Referenced from set '" + this.Name + "'");
                    }
                }

                // TODO: check if the found symbol's namespace matches the one we were searching for?
                // Probably not super important since there aren't many conflicts.

                if (!IsPropertyMultipleCardinality(namespaceDef, name) &&
                    this.childSets.FirstOrDefault(x => String.Equals(x.Name, name)) != null)
                {
                    throw new InvalidOperationException(String.Format("PropertySet {0} already contains a value for {1}", this.Name, name));
                }

                PropertySet childSet = new PropertySet((PropertySetDef)symDef);
                this.childSets.Add(childSet);

                return childSet;
            }
            else
            {
                // selectedSets should only contain one at this point.
                var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                        x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

                int count = selectedSets.Count();
                System.Diagnostics.Debug.Assert(count <= 1);
                if (count == 0)
                {
                    return CreateChildSet(names[nameIndex]).CreateChildSet(names, nameIndex + 1);
                }
                else
                {
                    System.Diagnostics.Debug.Assert(count == 1);
                    return selectedSets.First().CreateChildSet(names, nameIndex + 1);
                }
            }
        }

        public void AddChildSet(PropertySet childSet)
        {
            if (this.def.policy == Policy.Inclusive &&
                !this.def.properties.Contains(childSet.def.Id))
            {
                throw new InvalidOperationException(String.Format("{1} is not a valid child of PropertySet {0}", this.Name, childSet.Name));
            }

            if (!IsPropertyMultipleCardinality(childSet.def.namespaceDef, childSet.Name) &&
                this.childSets.FirstOrDefault(x => String.Equals(x.Name, childSet.Name)) != null)
            {
                throw new InvalidOperationException(String.Format("PropertySet {0} already contains a value for {1}", this.Name, childSet.Name));
            }

            this.childSets.Add(childSet);
        }

        public void AddChildProperty(Property childProperty)
        {
            if (this.def.policy == Policy.Inclusive &&
                !this.def.properties.Contains(childProperty.def.Id))
            {
                throw new InvalidOperationException(String.Format("{1} is not a valid child of PropertySet {0}", this.Name, childProperty.Name));
            }

            if (!IsPropertyMultipleCardinality(childProperty.def.namespaceDef, childProperty.Name) &&
                this.childSets.FirstOrDefault(x => String.Equals(x.Name, childProperty.Name)) != null)
            {
                throw new InvalidOperationException(String.Format("PropertySet {0} already contains a value for {1}", this.Name, childProperty.Name));
            }

            this.childProperties.Add(childProperty);
        }
        #endregion

        #region Internal
        internal PropertySet(Symbols symbols, string qualifiedname)
        {
            NamespaceDef namespaceDef;
            string name;

            symbols.GetNamespaceAndName(qualifiedname, out namespaceDef, out name);
            if (namespaceDef == null)
            {
                throw new InvalidOperationException("Invalid namespace");
            }

            this.def = namespaceDef.GetSymbolDef(name) as PropertySetDef;
            if (this.def == null)
            {
                throw new InvalidOperationException("Invalid symbol");
            }

            this.childProperties = new List<Property>();
            this.childSets = new List<PropertySet>();
        }

        internal PropertySet(PropertySetDef def)
        {
            this.def = def;
            this.childProperties = new List<Property>();
            this.childSets = new List<PropertySet>();
        }

        internal void WriteXML(NamespaceDef parentNamespace, XmlTextWriter xw)
        {
            string namespaceText = "";
            if (parentNamespace == null ||
                parentNamespace.GetSymbolDef(this.def.Id) == null)
            {
                namespaceText = this.def.namespaceDef.Name + ".";
            }

            xw.WriteStartElement(namespaceText + this.def.Name);

            foreach (Property prop in this.childProperties.Where(x => x.Attribute))
            {
                prop.WriteXML(this.def.namespaceDef, xw);
            }

            foreach (Property prop in this.childProperties.Where(x => !x.Attribute))
            {
                prop.WriteXML(this.def.namespaceDef, xw);
            }

            foreach (PropertySet set in childSets)
            {
                set.WriteXML(this.def.namespaceDef, xw);
            }

            xw.WriteEndElement();
        }

        internal void ReadXML(Symbols symbols, XmlTextReader xr)
        {
            NamespaceDef currentNamespace = this.def.namespaceDef;

            int initalDepth = xr.Depth;

            // Load attribute properties first
            while (xr.MoveToNextAttribute()) // Read the attributes.
            {
                string[] names = xr.LocalName.Split('.');
                string symbolName;
                if (names.Length > 1)
                {
                    // First look up the namespace
                    currentNamespace = symbols.GetNamespaceDef(names[0]);
                    symbolName = names[1];
                }
                else
                {
                    // Use the current namespace
                    symbolName = names[0];
                }

                SymbolDef childSymDef = currentNamespace.GetSymbolDef(symbolName);

                if (childSymDef.Type.Type != Type.PropertySet)
                {
                    Property childProp = new Property((PropertyDef)childSymDef);
                    childProp.ReadXML(xr);
                    this.childProperties.Add(childProp);
                }
                else
                {
                    throw new InvalidDataException("Invlide symbol type");
                }
            }
            xr.MoveToElement();

            if (!xr.IsEmptyElement)
            {
                // Now load the elements
                while (xr.Read())
                {
                    switch (xr.NodeType)
                    {
                        case XmlNodeType.Element: // The node is an element.
                            string[] names = xr.LocalName.Split('.');
                            string symbolName;
                            if (names.Length > 1)
                            {
                                // First look up the namespace
                                currentNamespace = symbols.GetNamespaceDef(names[0]);
                                symbolName = names[1];
                            }
                            else
                            {
                                // Use the current namespace
                                symbolName = names[0];
                            }

                            SymbolDef childSymDef = currentNamespace.GetSymbolDef(symbolName);
                            if (childSymDef == null)
                            {
                                throw new Exception("Unknown symbol found while parsing SimProp document: " + symbolName);
                            }

                            if (childSymDef.Type.Type == Type.PropertySet)
                            {
                                PropertySet childSet = new PropertySet((PropertySetDef)childSymDef);
                                childSet.ReadXML(symbols, xr);
                                childSets.Add(childSet);
                            }
                            else
                            {
                                Property childProp = new Property((PropertyDef)childSymDef);
                                childProp.ReadXML(xr);
                                this.childProperties.Add(childProp);
                            }

                            break;

                        case XmlNodeType.EndElement: // The node is an element.
                            if (initalDepth == xr.Depth)
                            {
                                return;
                            }

                            break;
                    }
                }
            }
        }

        internal void BuildGuidTable(OrderedDictionary<Guid, int> idToObjectSizeTable)
        {
            if (!idToObjectSizeTable.ContainsKey(this.def.Id))
            {
                idToObjectSizeTable.Add(this.def.Id, -1);
            }

            foreach (var prop in this.childProperties)
            {
                prop.BuildGuidTable(idToObjectSizeTable);
            }

            foreach (var set in childSets)
            {
                set.BuildGuidTable(idToObjectSizeTable);
            }
        }

        internal void WriteBinaryData(OrderedDictionary<Guid, int> idToObjectSizeTable, Format format, BinaryWriter bw)
        {
            // Write out header:
            bw.Write((Int32)idToObjectSizeTable.IndexOfKey(this.def.Id));
            if (format == Format.FlightBinary)
            {
                bw.Write((Int32)0);
            }

            MemoryStream ms = new MemoryStream();
            BinaryWriter childBW = new BinaryWriter(ms);

            foreach (var prop in this.childProperties)
            {
                prop.WriteBinaryData(idToObjectSizeTable, format, childBW);
            }

            foreach (var set in childSets)
            {
                set.WriteBinaryData(idToObjectSizeTable, format, childBW);
            }

            // Cap off stream with empty header
            childBW.Write((Int32)0);
            if (format == Format.FlightBinary)
            {
                childBW.Write((Int32)0);
            }

            bw.Write((Int32)ms.Length);
            bw.Write(ms.GetBuffer(), 0, (int)ms.Length);
        }

        internal void ReadBinaryData(Symbols symbols, OrderedDictionary<Guid, int> idToObjectSizeTable, Format format, BinaryReader br)
        {
            switch (this.def.Type.Type)
            {
                case Type.PropertySet:
                    {
                        if (format == Format.FlightBinary)
                        {
                            int unknown = br.ReadInt32();
                        }
                        int cb = br.ReadInt32();

                        long startPosition = br.BaseStream.Position;
                        while (br.BaseStream.Position < startPosition + cb)
                        {
                            int guidIndex = br.ReadInt32();
                            SymbolDef childSymDef = symbols.GetSymbolDef(idToObjectSizeTable.GetKeyByIndex(guidIndex));
                            if (childSymDef == null)
                            {
                                // This can occur if the schema doesn't match the file.  Most likely this is because the file is of an old format.
                                throw new InvalidDataException("The format of the SimProp file does not appear to match the schema.  Make sure you are not trying to convert an old file.  A symbol with ID '" + idToObjectSizeTable.GetKeyByIndex(guidIndex).ToString() + "' was not found.");
                            }
                            else if (childSymDef.Type.Type != Type.None)
                            {
                                if (childSymDef.Type.Type == Type.PropertySet)
                                {
                                    PropertySet childSet = new PropertySet((PropertySetDef)childSymDef);
                                    childSet.ReadBinaryData(symbols, idToObjectSizeTable, format, br);
                                    childSets.Add(childSet);
                                }
                                else
                                {
                                    Property childProp = new Property((PropertyDef)childSymDef);
                                    childProp.ReadBinaryData(idToObjectSizeTable, format, br);
                                    this.childProperties.Add(childProp);
                                }
                            }
                            else if (format == Format.FlightBinary && br.BaseStream.Position == startPosition + cb - 4)
                            {
                                // Should only happen at the end of a PropertySet
                                int unknown2 = br.ReadInt32();
                            }
                            else if (format == Format.FSXBinary && br.BaseStream.Position == startPosition + cb)
                            {
                                // Do nothing
                            }
                            else
                            {
                                throw new InvalidDataException("Error reading binary format.");
                            }
                        }
                    }
                    break;

                default:
                    throw new InvalidDataException("UNEXPECTED TYPE FOR PROPERTY SET");
            }
        }
        #endregion

        #region Private
        private PropertySetDef def;
        private List<Property> childProperties;
        private List<PropertySet> childSets;


        private IEnumerable<PropertySet> GetChildSets(string[] names, int nameIndex)
        {
            var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                    x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

            if (nameIndex == names.Length - 1)
            {
                return selectedSets;
            }
            else
            {
                // selectedSets should only contain one at this point.
                System.Diagnostics.Debug.Assert(selectedSets.Count() == 1);
                return selectedSets.First().GetChildSets(names, nameIndex + 1);
            }
        }

        private IEnumerable<Property> GetChildProperties(string[] names, int nameIndex)
        {
            if (nameIndex == names.Length - 1)
            {
                return this.childProperties.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                  x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));
            }
            else
            {
                // selectedSets should only contain one at this point.
                var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                        x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

                int count = selectedSets.Count();
                if (count == 0)
                {
                    return null;
                }
                else
                {
                    System.Diagnostics.Debug.Assert(selectedSets.Count() == 1);
                    return selectedSets.First().GetChildProperties(names, nameIndex + 1);
                }
            }
        }

        private bool RemoveChildProperties(string[] names, int nameIndex, bool removeAll)
        {
            if (nameIndex == names.Length - 1)
            {
                if (removeAll)
                {
                    return this.childProperties.RemoveAll(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase)) > 0;
                }
                else
                {
                    int index = this.childProperties.FindIndex(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));
                    if (index >= 0)
                    {
                        this.childProperties.RemoveAt(index);
                        return true;
                    }

                    return false;
                }
            }
            else
            {
                // selectedSets should only contain one at this point.
                var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                        x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

                System.Diagnostics.Debug.Assert(selectedSets.Count() == 1);
                return selectedSets.First().RemoveChildProperties(names, nameIndex + 1, removeAll);
            }
        }

        private bool RemoveChildSets(string[] names, int nameIndex, bool removeAll)
        {
            if (nameIndex == names.Length - 1)
            {
                if (removeAll)
                {
                    return this.childSets.RemoveAll(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase)) > 0;
                }
                else
                {
                    int index = this.childSets.FindIndex(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));
                    if (index >= 0)
                    {
                        this.childSets.RemoveAt(index);
                        return true;
                    }

                    return false;
                }
            }
            else
            {
                // selectedSets should only contain one at this point.
                var selectedSets = this.childSets.Where(x => x.Name.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase) ||
                                                        x.NamespacedName.Equals(names[nameIndex], StringComparison.OrdinalIgnoreCase));

                System.Diagnostics.Debug.Assert(selectedSets.Count() == 1);
                return selectedSets.First().RemoveChildProperties(names, nameIndex + 1, removeAll);
            }
        }
        #endregion
    }
}
